package com.ddwl.common.aop;

import brave.Tracer;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.ddwl.common.annotation.Auth;
import com.ddwl.common.constant.BaseEnum;
import com.ddwl.common.constant.OperateLogEnum;
import com.ddwl.common.constant.RabbitMqConstants;
import com.ddwl.common.util.HttpUtil;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.time.Instant;
import java.util.Map;

/**
 * @Author hemiaojun
 * @Date 2020/9/17 9:28
 */
@Slf4j
@Aspect
@Component
public class OperateLogAspect {

    @Resource
    private RabbitTemplate rabbitTemplate;
    @Resource
    private Tracer tracer;

    @Pointcut("within(com.ddwl..*.controller..*)")
    public void controllerPointcut() {
    }


    @AfterReturning("controllerPointcut()")
    public void doAfter(JoinPoint joinPoint){
        handleLog(joinPoint,null);
    }

    @AfterThrowing(pointcut = "controllerPointcut()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        try {
            this.handleLog(joinPoint, e);
        } catch (Exception ex) {
            log.error("aspect error", ex);
        }
    }

    private void handleLog(JoinPoint joinPoint, Throwable e) {
        try{
            JSONObject jsonObject = new JSONObject();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            String methodName=method.getName();
            ServletRequestAttributes sra= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request=  sra.getRequest();
            String product = request.getHeader("pk");
            Auth auth = method.getAnnotation(Auth.class);
            if (auth != null) {
                BaseEnum.ProductEnum productEnum = BaseEnum.ProductEnum.getProduct(product);
                if (productEnum == null) {
                    return;
                }
            }
            ApiOperation apiOperation=method.getAnnotation(ApiOperation.class);
            String clazzName = joinPoint.getTarget().getClass().getName();
            String userAgent = request.getHeader("User-Agent");
            String deviceId=request.getHeader("dk");
            String channel = request.getHeader("channel");
            String version = request.getHeader("version");
            String userId = request.getParameter("userId");
            String mobile = request.getParameter("mobile");
            String username = request.getParameter("username");
            if(StrUtil.isNotEmpty(mobile)){
                jsonObject.put("userName", mobile);
            }
            if(StrUtil.isNotEmpty(username)){
                jsonObject.put("userName", username);
            }
            if(StrUtil.isNotEmpty(userId)){
                jsonObject.put("userId", userId);
            }
            jsonObject.put("pk", product);
            jsonObject.put("channel", channel);
            jsonObject.put("version", version);

            Map<String, String> authMap = (Map<String, String>) request.getAttribute("authMap");
            jsonObject.put("dk", deviceId);
            if(null!=authMap){
                jsonObject.put("operateId", authMap.get("uid"));
                jsonObject.put("pt", authMap.get("pt"));
                if (StrUtil.isEmpty(jsonObject.getString("pk"))) {
                    jsonObject.put("pk", authMap.get("pk"));
                }
                if (StrUtil.isEmpty(jsonObject.getString("channel"))) {
                    jsonObject.put("channel", authMap.get("channel"));
                }
                if (StrUtil.isEmpty(jsonObject.getString("version"))) {
                    jsonObject.put("version", authMap.get("version"));
                }
                if(StrUtil.isEmpty(jsonObject.getString("dk"))){
                    jsonObject.put("dk", authMap.get("dk"));
                }
                jsonObject.put("operateName", authMap.get("nickname"));
                if(StrUtil.isNotEmpty(authMap.get("mobile"))){
                    jsonObject.put("userName", authMap.get("mobile"));
                }
                if(StrUtil.isNotEmpty(authMap.get("userName"))){
                    jsonObject.put("userName", authMap.get("userName"));
                }
            }
            String uri=request.getRequestURI();
            if(StrUtil.isNotEmpty(OperateLogEnum.typeEnum.getTypeCode(uri))){
                jsonObject.put("type", OperateLogEnum.typeEnum.getTypeCode(uri));
            }
            jsonObject.put("uri",uri);
            jsonObject.put("method",clazzName+"."+methodName);
            jsonObject.put("methodType",request.getMethod());
            if (null != apiOperation) {
                jsonObject.put("description",apiOperation.value());
            }
            jsonObject.put("ip",HttpUtil.getIp(request));
            jsonObject.put("userAgent",userAgent);
            jsonObject.put("operateTime",Instant.now());
            jsonObject.put("traceId",tracer.currentSpan().context().traceIdString());
            if (null == e) {
                jsonObject.put("status",OperateLogEnum.statusEnum.SUCCESS.getCode());
            } else {
                jsonObject.put("status",OperateLogEnum.statusEnum.FAILURE.getCode());
                StringWriter sw = new StringWriter();
                PrintWriter pw = new PrintWriter(sw);
                e.printStackTrace(pw);
                String errorMsg = sw.toString();
                if (StringUtils.hasText(errorMsg)) {
                    if (errorMsg.length() > 256) {
                        errorMsg = errorMsg.substring(0, 256);
                    }
                    jsonObject.put("ex",errorMsg);
                }
            }
            rabbitTemplate.convertAndSend(RabbitMqConstants.OPERATE_LOG_QUEUE,jsonObject.toString());
        } catch (Exception exception) {
           log.error(exception.getMessage());
        }

    }

}
